home *** CD-ROM | disk | FTP | other *** search
- ;;; gnus-mime.el
- ;;; Support to read/post MIME format USENET articles in GNUS.
- ;;; Version 0.2
-
- ;; Author Spike <Spike@world.std.com>
- ;; with code from Michael Littman's <mlittman@breeze.bellcore.com>
- ;; richtext.el and metamail's MH-E patches.
-
-
- ;; This requires that you have the metamail package installed
- ;; (thumper.bellcore.com:/pub/nsb/mm.tar.Z) and transparent.el
-
- ;; This package provides five basic functions
- ;;
- ;; gnus-Subject-run-metamail - invokes metamail on the selected news article.
- ;; gnus-inews-article - replaces the standard gnus-inews-article with one
- ;; which inserts MIME headers and does Richtext style
- ;; signatures. It also supports multi-media signatures
- ;; if ".signature-MIME" or ".signature-distriubtion-MIME"
- ;; exists, it is inserted and any line which reads:
- ;; --MIME-BOUNDARY
- ;; is replaced with the current boundary.
- ;; gnus-richtext-posting - converts the posting buffer to Richtext format,
- ;; knows how to make text bold, italics, and
- ;; underlined.
- ;; gnus-insert-file-as-mime - Allows you to insert arbitrary data into
- ;; a posting in MIME format. Automatically
- ;; recognizes some formats (GIF, JPEG, PS),
- ;; more can be add through "auto-mime-id-list".
- ;; gnus-insert-file-as-mime-external - Allows you to create a reference to
- ;; an external file.
-
- ;; As shiped this binds gnus-Subject-run-metamail to "@" in the "*Subject*"
- ;; buffer. gnus-richtext-posting to "C-c r", "gnus-insert-file-as-mime" to
- ;; "C-c i", and "gnus-insert-file-as-mime -external" to "C-c e" in the posting
- ;; buffer.
- ;;
-
- ;; To use put "(load-library "gnus-mime.el")" in your ".emacs" or "default.el"
-
- ;; If you want GNUS to announce MIME postings but something like:
- ;; (setq gnus-Article-prepare-hook
- ;; '(lambda ()
- ;; (gnus-Subject-check-content-type)))
- ;; in your ".emacs" file.
-
- ;; CAVEATS: You can not call gnus-richtext-posting after calling
- ;; gnus-insert-file-as-mime or gnus-insert-file-as-mime-external
-
- (require 'transparent)
- (load-library "rnewspost") ;; sigh... This could be better.
- (require 'gnuspost)
- (provide 'gnus-mime)
-
- (defvar gnus-invoke-mime-key "@"
- "The key that calls gnus-Subject-run-metamail")
-
- (define-key gnus-Subject-mode-map gnus-invoke-mime-key
- 'gnus-Subject-run-metamail)
-
- (define-key news-reply-mode-map "\C-cr" 'gnus-richtext-posting)
- (define-key news-reply-mode-map "\C-ci" 'gnus-insert-file-as-mime)
- (define-key news-reply-mode-map "\C-ce" 'gnus-insert-file-as-mime-external)
-
- (defvar auto-mime-id-list nil "\
- A list of filename patterns vs corresponding MIME type strings
- Each element looks like (REGEXP . TYPE).")
- (setq auto-mime-id-list (mapcar 'purecopy
- '(("\\.gif$" . "image/gif")
- ("\\.jpg$" . "image/jpeg")
- ("\\.xwd$" . "image/x-xwd")
- ("\\.ps$" . "application/PostScript"))))
-
- ;;;;;;
-
- (defun gnus-Subject-check-content-type ()
- (if (gnus-fetch-field "Mime-Version")
- (let ((content-type (gnus-fetch-field "Content-Type")))
- (message (concat "You can use '" gnus-invoke-mime-key
- "' to view this '" content-type
- "' MIME format article.")))))
-
- (defun gnus-Subject-run-metamail ()
- (interactive)
- "Process Selected Article Through \"metamail\"."
- (gnus-Subject-select-article)
- (gnus-eval-in-buffer-window gnus-Article-buffer
- (let ((metamail-tmpfile (make-temp-name "/tmp/rmailct")))
- (save-restriction
- (widen)
- (write-region (point-min) (point-max) metamail-tmpfile))
- (if
- (and window-system (getenv "DISPLAY"))
- (let ((buffer-read-only nil))
- (push-mark (point) t)
- (erase-buffer)
- (call-process "metamail" nil t t
- "-m" "mh-e" "-x" "-d" "-q" "-z" metamail-tmpfile)
- (exchange-point-and-mark)
- (set-buffer-modified-p nil)
- (other-window -1))
- (progn
- (other-window -1)
- (switch-to-buffer "METAMAIL")
- (erase-buffer)
- (sit-for 0)
- (transparent-window
- "METAMAIL"
- "metamail"
- (list "-p" "-d" "-q" metamail-tmpfile)
- nil
- (concat
- "\n\r\n\r*****************************************"
- "*******************************\n\rPress any key "
- "to go back to EMACS\n\r\n\r***********************"
- "*************************************************\n\r")))
- )
- )
- )
- )
-
-
- (defvar rich-substitutions
- '(
- ("<" "<lt>") ; in case some one sends less-thans.
- ("\\B%\\b" "</italic>") ; needs to be first to not get closing tags.
- ("\\b%\\B" "<italic>")
- ("\\B\\*\\b" "<bold>")
- ("\\b\\*\\B" "</bold>")
- ("
- " "
- <nl>")
- ("\\B_\\b" "<underline>")
- ("\\b_\\B" "</underline>")
- )
- "A table of REGEXP to translate text to MIME's text/richtext format.")
-
- (defun gnus-richtext-posting ()
- "Convert the current buffer to MIME's \"text/richtext\" format.
- \"*foo*\" is converted to bold, \"%foo%\" to italics, and \"_foo_\" to
- underlined. Note: this does not recognize font markers *after*
- punctuation, thus \"*foo!*\" will not work."
- (interactive)
- (mail-position-on-field "Subject")
- (or (gnus-fetch-field "Mime-Version")
- (insert "\nMime-Version: 1.0"))
- (or (gnus-fetch-field "Content-Type")
- (insert "\nContent-Type: text/richtext"))
- (goto-char (point-min))
- (search-forward (concat "\n" mail-header-separator "\n") nil t)
- (perform-rich-sub)
- )
-
- (defun perform-rich-sub ()
- "Perform the rich substiution."
- (let ((subs rich-substitutions)
- pat rep
- (top (point)))
- (save-excursion
- (while subs
- (setq pat (car (car subs)))
- (setq rep (car (cdr (car subs))))
- (setq subs (cdr subs))
- (goto-char top)
- (while (re-search-forward pat (point-max) t)
- (replace-match rep))
- ))))
-
- (defun gnus-insert-file-as-mime (filename)
- "Encode and insert a file into the posting buffer and setup the correct
- MIME headers for that file type."
- (interactive "FFind file: ")
- (let ((ctype nil)
- (binary nil)
- (boundary nil))
- (setq ctype (gnus-get-mime-content-type filename))
- (setq boundary (gnus-fetch-or-create-boundary))
- (goto-char (point-max))
- (search-backward boundary (point-min) t)
- (forward-line)
- (insert-file filename)
- (save-excursion
- (if (re-search-forward "[\200-\377]" nil t)
- (setq binary t)))
- (if binary
- (save-excursion
- (shell-command-on-region (point) (mark) "mmencode" t)))
- (insert (concat "Content-type: " ctype "\n"))
- (insert "Content-Transfer-Encoding: ")
- (if binary
- (insert "base64\n\n")
- (insert "7BIT\n\n"))
- (goto-char (point-max))
- (insert (concat "\n--" boundary "\n"))
- ))
-
- (defun gnus-inews-article ()
- "NNTP inews interface."
- (let ((signature
- (if gnus-signature-file
- (expand-file-name gnus-signature-file nil)))
- (distribution nil)
- (artbuf (current-buffer))
- (tmpbuf (get-buffer-create " *GNUS-posting*"))
- (ctype nil)
- (boundary nil))
- (save-excursion
- (set-buffer tmpbuf)
- (buffer-flush-undo (current-buffer))
- (erase-buffer)
- (insert-buffer-substring artbuf)
- ;; Get distribution.
- (setq distribution (gnus-fetch-field "Distribution"))
- (if signature
- (progn
- ;; Change signature file by distribution.
- ;; Suggested by hyoko@flab.fujitsu.junet.
- (if (file-exists-p (concat signature "-" distribution))
- (setq signature (concat signature "-" distribution)))
- ;; Insert signature.
- (if (file-exists-p (concat signature "-MIME"))
- ;; Random MIME format signature
- (progn
- (setq boundary (gnus-fetch-or-create-boundary))
- (goto-char (point-max))
- (insert-file-contents (concat signature "-MIME"))
- (while (re-search-forward "^--MIME-BOUNDARY$" (point-max) t)
- (replace-match (concat "--" boundary) t))
- (goto-char (point-max))
- (insert (concat "\n--" boundary "\n")))
- ;; else "normal" signature
- (if (file-exists-p signature)
- (progn
- ;; Use richtext signature format if possable.
- (if (setq boundary (gnus-fetch-boundary))
- (progn
- (goto-char (point-max))
- (insert "Content-type: text/richtext\n")
- (insert "Content-Transfer-Encoding: quoted-printable\n\n")
- ))
- (if (or boundary
- (string-equal (gnus-fetch-field "Content-Type")
- "text/richtext"))
- (progn
- (goto-char (point-max))
- (insert "<signature>")
- (insert-file-contents signature)
- (goto-char (point-max))
- (insert "</signature>\n")
- (insert (concat "--" boundary "\n")))
- (progn
- (goto-char (point-max))
- (insert "--\n")
- (insert-file-contents signature)))
- )))))
- ;; Prepare article headers.
- (save-restriction
- (goto-char (point-min))
- (search-forward "\n\n")
- (narrow-to-region (point-min) (point))
- (gnus-inews-insert-headers)
- ;; insert mime headers if needed.
- (goto-char (point-max))
- (forward-line -2)
- (or (gnus-fetch-field "Mime-Version")
- (insert "Mime-Version: 1.0\n"))
- (or (gnus-fetch-field "Content-Type")
- (insert "Content-Type: text\n"))
- ;; Save author copy of posted article. The article must be
- ;; copied before being posted because `gnus-request-post'
- ;; modifies the buffer.
- (let ((case-fold-search t))
- ;; Find and handle any FCC fields.
- (goto-char (point-min))
- (if (re-search-forward "^FCC:" nil t)
- (gnus-inews-do-fcc))))
- (widen)
- ;; Run final inews hooks.
- (run-hooks 'gnus-Inews-article-hook)
- ;; Post an article to NNTP server.
- ;; Return NIL if post failed.
- (prog1
- (gnus-request-post)
- (kill-buffer (current-buffer)))
- )))
-
- (defun gnus-insert-file-as-mime-external ()
- "Setup an external Content-Type header"
- (interactive)
- (let ((access-type)
- (site nil)
- (directory nil)
- (filename nil)
- (ftp-mode nil)
- (ctype nil)
- (server nil)
- (encoding nil)
- (access-types-list
- '(("ftp") ("anon-ftp") ("tftp") ("afs") ("local-file")
- ("mail-server"))))
- (setq access-type (completing-read "access-type: " access-types-list
- nil t nil))
- (cond
-
- ((or (string-equal access-type "ftp")
- (string-equal access-type "anon-ftp"))
- (setq site (read-string "The hostname of the FTP site: "))
- (setq directory
- (read-string
- "The directory containing the file (Hit Enter for top-level): "))
- (setq filename (read-string "The name of the file: "))
- (setq ftp-mode (completing-read "FTP transfer type: "
- '(("image") ("ascii") ("ebcdic"))
- nil t nil))
- )
- ((or (string-equal access-type "local-file")
- (string-equal access-type "afs"))
- (setq filename
- (expand-file-name
- (read-file-name "The full pathname of the file: " nil nil t)))
- )
- ((string-equal access-type "mail-server")
- (setq server (read-string "The Email address of the mail server: "))
- )
- )
- (setq ctype (gnus-get-mime-content-type filename))
- (setq encoding (completing-read "Encoding of remote file: "
- '(("none") ("base64")
- ("uuencode") ("quoted-printable"))
- nil t nil))
- (if (equal encoding "none") (setq encoding nil))
- (setq boundary (gnus-fetch-or-create-boundary))
- (goto-char (point-max))
- (search-backward boundary (point-min) t)
- (forward-line)
- (insert "Content-type: message/external-body;\n")
- (insert (concat "\taccess-type=\"" access-type "\""))
- (if filename
- (insert (concat ";\n\tname=\"" filename "\"")))
- (if site
- (insert (concat ";\n\tsite=\"" site "\"")))
- (if directory
- (insert (concat ";\n\tdirectory=\"" directory "\"")))
- (if ftp-mode
- (insert (concat ";\n\tmode=\"" ftp-mode "\"")))
- (if server
- (insert (concat ";\n\tserver=\"" server "\"")))
- (insert (concat "\n\nContent-type: " ctype "\n"))
- (if encoding
- (insert (concat "Content-Transfer-Encoding: " encoding "\n"))
- )
- (insert "\n")
- (goto-char (point-max))
- (insert (concat "--" boundary "\n"))
- (if (string-equal access-type "mail-server")
- (progn
- (forward-line -2)
- (insert "\n\n")
- (forward-line -1)
- (message "Now enter the commands to pass to the mail server")))
- )
- )
-
- (defun gnus-fetch-boundary ()
- "Return the boundary or nil if we are not a mulitpart message"
- (let ((boundary nil)
- (ctype (gnus-fetch-field "Content-Type")))
- (if (and ctype (string-match "multipart" ctype))
- (progn
- (string-match "boundary=\"" ctype)
- (setq boundary (substring ctype (match-end 0)))
- (string-match "\"" boundary)
- (setq boundary
- (substring boundary 0 (- (match-end 0) 1)))))
- boundary)
- )
-
- (defun gnus-fetch-or-create-boundary ()
- "Return the boundary or create one."
- (let
- ((boundary nil)
- (encoding nil)
- (ctype nil))
- (if (not (setq boundary (gnus-fetch-boundary)))
- (progn
- (setq boundary
- (concat
- "GNUS.BOUNDARY." (system-name) "." (current-time-string)))
- (save-excursion
- (mail-position-on-field "Subject")
- (or (gnus-fetch-field "Mime-Version")
- (insert "\nMime-Version: 1.0\n"))
- ;; If there is alread a Content-Type header, wrap the existing
- ;; data in boundaries, moving the old Content* headers inside
- ;; the boundary. We won't get here if it was already a "mixed"
- ;; type.
- (if (setq ctype (gnus-fetch-field "Content-Type"))
- (progn
- (setq encoding
- (gnus-fetch-field "Content-Transfer-Encoding"))
- (mail-position-on-field "Content-Type")
- (beginning-of-line)
- (delete-region (point) (progn (forward-line 1) (point)))
- (mail-position-on-field "Content-Transfer-Encoding")
- (beginning-of-line)
- (delete-region (point) (progn (forward-line 1) (point))))
- (progn
- (setq ctype "text")
- (setq encoding "7BIT")))
- (goto-char (point-min))
- (re-search-forward
- (concat "^" (regexp-quote mail-header-separator) "\n"))
- (insert (concat "--" boundary "\n"))
- (insert (concat "Content-type: " ctype "\n"))
- (insert (concat "Content-Transfer-Encoding: " encoding "\n"))
- (goto-char (point-max))
- (insert (concat "\n--" boundary "\n"))
- (mail-position-on-field "Mime-Version")
- (forward-line)
- (insert (concat "Content-Type: multipart/mixed;\n"
- "\tboundary=\"" boundary "\"")))))
- boundary)
- )
-
- (defun gnus-get-mime-content-type (filename)
- "Return the Content-Type of a FILENAME, asking the user if need be."
- (let ((mlist auto-mime-id-list)
- (ctype nil)
- (name filename))
- (if filename
- (while (and (not ctype) mlist)
- (if (string-match (car (car mlist)) name)
- (setq ctype (cdr (car mlist))))
- (setq mlist (cdr mlist)))
- )
- (if (not ctype)
- (setq ctype
- (read-string "MIME content type: " "application/octet-stream")))
- ctype)
- )
-